package com.uxsino.commons.db.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.uxsino.commons.db.model.AlertStrategy;
import com.uxsino.commons.db.model.AlertStrategyItme;
import com.uxsino.commons.db.model.IntervalType;
import com.uxsino.commons.db.model.Occasion;
import com.uxsino.commons.db.model.SendTimeWay;
import com.uxsino.commons.db.model.SimoMsg;
import com.uxsino.commons.db.redis.service.EventTemplateRedis;
import com.uxsino.commons.db.redis.service.SiteUserRedis;

/**
 * 
 * @description 推送校验
 * @date 2019年6月6日
 */
@Component
public class OccasionValidator {

    private static Logger logger = LoggerFactory.getLogger(OccasionValidator.class);

    @Autowired
    private SiteUserRedis userRedis;

    @Autowired
    private EventTemplateRedis eventTemplateRedis;

    /**
     * 校验【产生立即告警】条件
     * @param eventInfo 事件模板信息
     * @param date 告警时间
     * @return
     */
    public boolean checkNowOccasion(AlertStrategyItme eventInfo, Date date) {
        if (SendTimeWay.CUSTOM.equals(eventInfo.getSendTime())) {
            // 若是立即告警又设置了自定义告警时间段，则在时间段里则立即告警
            JSONObject sendTimeInfo = JSONObject.parseObject(eventInfo.getSendTimeInfo());
            IntervalType intervalType = IntervalType.valueOf(sendTimeInfo.getString("type"));
            JSONArray infos = sendTimeInfo.getJSONArray("info");
            return ifSendAlert(sendTimeInfo, infos, date, intervalType);
        }
        // 立即告警又是7*24那就马上告警
        return true;
    }

    /**
     * 校验【只在首次产生告警时发通知】条件
     * @param eventInfo 事件模板信息
     * @param date 告警时间
     * @param isNew 是否新告警
     * @return
     */
    public boolean checkFirstOccasion(AlertStrategyItme eventInfo, Date date, boolean isNew) {
        if (!isNew) {
            return false;
        }
        if (SendTimeWay.CUSTOM.equals(eventInfo.getSendTime())) {
            // 若是【只在首次产生告警时发通知】又设置了自定义告警时间段，则在时间段里则立即告警
            JSONObject sendTimeInfo = JSONObject.parseObject(eventInfo.getSendTimeInfo());
            IntervalType intervalType = IntervalType.valueOf(sendTimeInfo.getString("type"));
            JSONArray infos = sendTimeInfo.getJSONArray("info");
            return ifSendAlert(sendTimeInfo, infos, date, intervalType);
        }
        // 新告警又是7*24那就马上告警
        return true;
    }

    /**
     * 比较间隔时间条件
     * @param recentAlertDate 最近告警时间
     * @param date 告警时间
     * @param intervalTime 间隔数
     * @param intervalTimeUnit 间隔单位：分钟/小时/天
     * @return
     */
    public boolean compareDatesInterval(Date recentAlertDate, Date date, double intervalTime,
        IntervalType intervalTimeUnit) {
        Long time = 0L;
        if (IntervalType.minute.equals(intervalTimeUnit)) {
            time = (long) (intervalTime * 60 * 1000);
        } else if (IntervalType.hour.equals(intervalTimeUnit)) {
            time = (long) (intervalTime * 60 * 60 * 1000);
        } else if (IntervalType.day.equals(intervalTimeUnit)) {
            time = (long) (intervalTime * 24 * 60 * 60 * 1000);
        }
        if (date.getTime() - recentAlertDate.getTime() < time) {
            return false;
        }
        return true;
    }

    /**
     * 解析告警时间
     * @param intervalTime 间隔数
     * @param intervalTimeUnit 间隔单位：分钟/小时/天
     * @param date 告警时间
     * @return
     */
    public Date analyzeInterval(double intervalTime, IntervalType intervalTimeUnit, Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (IntervalType.minute.equals(intervalTimeUnit)) {
            calendar.add(Calendar.MINUTE, (int) intervalTime);
        } else if (IntervalType.hour.equals(intervalTimeUnit)) {
            calendar.set(Calendar.HOUR_OF_DAY, (int) (calendar.get(Calendar.HOUR_OF_DAY) + intervalTime));
        } else if (IntervalType.day.equals(intervalTimeUnit)) {
            calendar.add(Calendar.DATE, (int) intervalTime);
        }
        return calendar.getTime();
    }

    /**
     * 告警时段校验
     * @param sendTimeInfo 发送告警的时间信息
     * @param infos 时间段信息，每周/每天：几点-几点
     * @param date 告警时间
     * @param intervalType 间隔类型：周/天
     * @return
     */
    public boolean ifSendAlert(JSONObject sendTimeInfo, JSONArray infos, Date date, IntervalType intervalType) {
        return (isSendTime(sendTimeInfo) && isInSendTime(infos, date, intervalType))
                || (!isSendTime(sendTimeInfo) && !isInSendTime(infos, date, intervalType));
    }

    /**
     * 是否告警时段
     * @param sendTimeInfo 发送告警的时间信息
     * @return
     */
    public boolean isSendTime(JSONObject sendTimeInfo) {
        boolean isSendTime = true;
        if ("no".equalsIgnoreCase(sendTimeInfo.getString("isSendTime"))) {
            isSendTime = false;
        }
        return isSendTime;
    }

    /**
     * 是否在该时段内
     * @param infos 时间段信息，每周/每天：几点-几点
     * @param date 告警时间
     * @param intervalType 间隔类型：周/天
     * @return
     */
    public boolean isInSendTime(JSONArray infos, Date date, IntervalType intervalType) {
        boolean isInSendTime = false;
        if (null == infos || infos.size() == 0) {
            return true;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        for (int i = 0; i < infos.size(); i++) {
            JSONObject info = infos.getJSONObject(i);
            if (StringUtils.isEmpty(info.getString("beginTime")) || StringUtils.isEmpty(info.getString("endTime"))) {
                continue;
            }
            Date from = new Date();
            Date to = new Date();
            try {
                from = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(new SimpleDateFormat("yyyy-MM-dd HH:mm").format(
                    from).split(" ")[0]
                        + " " + info.getString("beginTime"));
                to = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(new SimpleDateFormat("yyyy-MM-dd HH:mm").format(to)
                    .split(" ")[0] + " " + info.getString("endTime"));
            } catch (ParseException e) {
                logger.error("日期转换失败:", e);
            }
            if (IntervalType.day.equals(intervalType)) {
                if (date.getTime() > from.getTime() && date.getTime() < to.getTime()) {
                    isInSendTime = true;
                    break;
                }
            } else if (IntervalType.week.equals(intervalType)) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                int w = cal.get(Calendar.DAY_OF_WEEK) - 2;
                if (w < 0)
                    w = 0;
                if (w == info.getIntValue("week") && date.getTime() > from.getTime() && date.getTime() < to.getTime()) {
                    isInSendTime = true;
                    break;
                }
            }
        }
        return isInSendTime;
    }

    /**
     * 组装接收详情
     * @param itme 事件模板信息
     * @param receiver 接收详情
     */
    @SuppressWarnings("unchecked")
    public void makeupReceiver(AlertStrategyItme itme, JSONObject receiver) {
        String[] notifyWayArray = itme.getNotifyWay().split(",");
        Set<Long> userIds = new HashSet<>();
        Set<String> emails = new HashSet<>();
        Set<String> phones = new HashSet<>();
        for (String str : itme.getUserIds().split(",")) {
            Long userId = Long.valueOf(str);
            JSONObject user = getUserById(userId);
            if (user == null || !"OK".equals(user.getString("status"))) {
                continue;
            }
            userIds.add(userId);
            if (!StringUtils.isEmpty(user.getString("email"))) {
                emails.add(user.getString("email"));
            }
            if (!StringUtils.isEmpty(user.getString("phone"))) {
                phones.add(user.getString("phone"));
            }
        }
        if (userIds.isEmpty()) {
            return;
        }
        for (String notifyWay : notifyWayArray) {
            notifyWay = "CAROUSEL".equals(notifyWay) || "POPUP".equals(notifyWay) ? "MSG" : notifyWay;
            switch (notifyWay) {
            case "MSG":
                Set<Long> socketUserIds = (Set<Long>) receiver.getOrDefault("socket", new HashSet<>());
                socketUserIds.addAll(userIds);
                receiver.put("socket", socketUserIds);
                break;
            case "APP":
                Set<Long> appUserIds = (Set<Long>) receiver.getOrDefault("app", new HashSet<>());
                appUserIds.addAll(userIds);
                receiver.put("app", appUserIds);
                break;
            case "MAIL":
                Set<String> emailSet = (Set<String>) receiver.getOrDefault("email", new HashSet<>());
                emailSet.addAll(emails);
                receiver.put("email", emailSet);
                break;
            case "SMS":
                Set<String> phoneSet = (Set<String>) receiver.getOrDefault("sms", new HashSet<>());
                phoneSet.addAll(phones);
                receiver.put("sms", phoneSet);
                break;
            case "MP":
                Set<Long> mpSet = (Set<Long>) receiver.getOrDefault("mp", new HashSet<>());
                mpSet.addAll(userIds);
                receiver.put("mp", mpSet);
                break;
            default:
                break;
            }
        }
    }

    /**
     * 获取用户信息
     * @param userId 用户ID
     * @return
     */
    private JSONObject getUserById(Long userId) {
        return JSONObject.parseObject(userRedis.get(userId + ""));
    }

    /**
     * 组装SIMO消息
     * @param msgFrom 消息来源
     * @param msgType 消息类型
     * @param msgTime 消息时间
     * @param hashKey 消息分组id
     * @param uid 消息的唯一标识
     * @param content 消息内容
     * @param extParam 扩展参数，用于前端渲染、跳转：{"level":2, "alertType":"Alert", "alertId":1, "isAvailability":false}
     * @param eventId 事件模板ID
     * @return
     */
    public SimoMsg getSimoMsg(String msgFrom, String msgType, Date msgTime, String hashKey, String uid, String content,
        JSONObject extParam, Long eventId) {
        if (eventId == null) {
            return null;
        }
        String eventStr = eventTemplateRedis.get(eventId.toString());
        if (StringUtils.isEmpty(eventStr)) {
            return null;
        }
        AlertStrategy strategy = JSONObject.parseObject(eventStr, AlertStrategy.class);
        if (strategy == null || strategy.getItmes() == null || strategy.getItmes().isEmpty()) {
            return null;
        }
        JSONObject receiver = new JSONObject();
        for (AlertStrategyItme itme : strategy.getItmes()) {
            if (itme == null || 1 != itme.getStatus() || StringUtils.isEmpty(itme.getNotifyWay())
                    || StringUtils.isEmpty(itme.getUserIds())) {
                continue;
            }
            boolean ifSendMsg = false;
            // 消息属于一次性发送，只需校验【产生立即告警】【只在首次产生告警时发通知】这2种情况
            if (Occasion.NOW.equals(itme.getOccasion())) {
                ifSendMsg = checkNowOccasion(itme, msgTime);
            } else if (Occasion.FIRST.equals(itme.getOccasion())) {
                ifSendMsg = checkFirstOccasion(itme, msgTime, true);
            }
            if (ifSendMsg) {
                makeupReceiver(itme, receiver);
            }
        }
        if (receiver.isEmpty()) {
            return null;
        }
        return new SimoMsg(msgFrom, msgType, msgTime, hashKey, uid, content, receiver, extParam);
    }

}
